{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Segmented Buffer Example\n",
    "\n",
    "The segmented buffer splits the ChipWhisperer-Lite/Pro sample buffer (24K/100K) into little segments. When you are doing a capture of small traces (such as with Hardware AES), you end up with a case where our buffer looks like this:\n",
    "\n",
    "                [ ~~~~~~................................................... ]\n",
    "                     |                      |\n",
    "    One power trace -+        Empty Space --+\n",
    "   \n",
    "We need to have the overhead of unload the buffer for that little bit of data. With segmented capture, we instead do this:\n",
    "\n",
    "                [ ~~~~~~|~~~~~~|~~~~~~|~~~~~~|~~~~~~|~~~~~~|~~~~~~|~~~~~~|~ ]\n",
    "                    |       |              \\ /                            |\n",
    "    Power trace  1 -+       |               |             Partial trace---+\n",
    "    Power trace 2 ----------+               |\n",
    "    Power traces 3..4..etc -----------------+\n",
    "    \n",
    "We'll show an example of this with the STM32F415 device.\n",
    "\n",
    "This will require a special operating mode that re-runs encryptions a number of times, so we don't have the serial protocol overhead (which would slow our capture down). You may want to modify the firmware to have less overhead in calling the hardware AES core as well.\n",
    "\n",
    "This segmented run mode was added in recent `simpleserial-aes` firmwares, you can see the commands added:\n",
    "\n",
    "    simpleserial_addcmd('s', 2, enc_multi_setnum);\n",
    "    simpleserial_addcmd('f', 16, enc_multi_getpt);\n",
    "\n",
    "The multiencrypt just runs the encryption with the new input being the old output:\n",
    "\n",
    "    for(unsigned int i = 0; i < num_encryption_rounds; i++){\n",
    "        trigger_high();\n",
    "        aes_indep_enc(pt);\n",
    "        trigger_low();\n",
    "    }"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## STM32F415 Hardware Programming / Setup\n",
    "\n",
    "This example uses the STM32F415 because it supports hardware AES. The segmented capture only provides serious speed-ups when the encryption itself is already very fast. The default software AES won't provide as dramatic a speed-up.\n",
    "\n",
    "Note you can use the \"simpleserial\" protocol V2 (SS_V2), which uses binary communications. This will further improve the speed-up by giving you less overhead for loading/unloading data.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "SCOPETYPE = 'OPENADC'\n",
    "PLATFORM = 'CW308_STM32F4'\n",
    "CRYPTO_TARGET = 'HWAES'\n",
    "num_traces = 50\n",
    "CHECK_CORR = False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash -s \"$PLATFORM\" \"$CRYPTO_TARGET\"\n",
    "cd ../../firmware/mcu/simpleserial-aes\n",
    "make PLATFORM=$1 CRYPTO_TARGET=$2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run \"../Setup_Scripts/Setup_Generic.ipynb\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fw_path = '../../firmware/mcu/simpleserial-aes/simpleserial-aes-{}.hex'.format(PLATFORM)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cw.program_target(scope, prog, fw_path)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## FPGA Version Note\n",
    "\n",
    "An updated bitstream was previously required for this - if you are on latest (from git) ChipWhisperer, this will be loaded automatically. If you wish to test other bitstream configurations, note you can load it with:\n",
    "\n",
    "    scope.reload_fpga(\"cwlite_interface.bit\")\n",
    "    %run \"../Setup_Scripts/Setup_Generic.ipynb\"\n",
    "\n",
    "But this is **not required to continue**."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Warning on sample size"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There is some bug with the sample size - you must set `scope.adc.samples` above some threshold (appears to be around 240). If you set it too low you'll get an error about insufficient data returned - cause is under investigation still. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Capture Example - Multiencryption Command\n",
    "\n",
    "The following will capture the first 1200 points of a trace, and record it. We'll use the multiencryption commands to improve the speed which we cause encryptions to occur."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.adc.samples = 1200"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.adc.fifo_fill_mode = \"segment\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import struct\n",
    "import time\n",
    "\n",
    "#IMPORTANT - we now need to generate enough triggers such that scope.adc.samples * NUM_TRIGGERS > max_fifo_size\n",
    "#            If not the HW won't exit capture mode. In this example code we weill just call the function so\n",
    "#            many times.\n",
    "max_fifo_size = scope.adc.oa.hwMaxSamples\n",
    "segments_to_capture = round(max_fifo_size / scope.adc.samples + 1)\n",
    "\n",
    "target.simpleserial_write('s', struct.pack(\">H\", segments_to_capture))\n",
    "\n",
    "start = time.time() #perf reasons only\n",
    "\n",
    "scope.arm()\n",
    "target.simpleserial_write('f', bytearray([0]*16))\n",
    "scope.capture_segmented() \n",
    "\n",
    "target.simpleserial_read('r', target.output_len)\n",
    "\n",
    "# Get segments now\n",
    "segs = scope.get_last_trace_segmented()\n",
    "\n",
    "end = time.time() #perf reasons only\n",
    "\n",
    "print(\"Captured %d segments of %d points each: \"%(len(segs), len(segs[0])))\n",
    "print(\"  %fs time\"%(end-start))\n",
    "print(\"  %d traces/second\"%(len(segs) / (end-start)))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib notebook\n",
    "import matplotlib.pylab as plt\n",
    "\n",
    "#For reference - you can see all the traces here as one (how it's read from teh buffer)\n",
    "\n",
    "wave = scope.get_last_trace()\n",
    "plt.figure()\n",
    "plt.plot(wave)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Example of reading the segments with `get_last_trace_segmented()` that does the work for you.\n",
    "plt.figure()\n",
    "for seg in segs:\n",
    "    plt.plot(seg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example with slow target communications\n",
    "\n",
    "The following uses the normal serial code. You'll find the overhead kills you - you only get ~42 traces/second. You can improve that by switching to SS_V2 which is a binary protocol. But the speed will still be limited here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import time\n",
    "#IMPORTANT - we now need to generate enough triggers such that scope.adc.samples * NUM_TRIGGERS > max_fifo_size\n",
    "#            If not the HW won't exit capture mode. In this example code we weill just call the function so\n",
    "#            many times.\n",
    "max_fifo_size = scope.adc.oa.hwMaxSamples\n",
    "segments_to_capture = round(max_fifo_size / scope.adc.samples + 1)\n",
    "\n",
    "start = time.time() #perf reasons only\n",
    "\n",
    "scope.arm()\n",
    "\n",
    "for i in range(0, segments_to_capture):\n",
    "    target.simpleserial_write('p', bytearray([0]*16))\n",
    "    target.simpleserial_read('r', target.output_len)\n",
    "\n",
    "capdone = time.time() #perf reasons only\n",
    "\n",
    "scope.capture_segmented() \n",
    "\n",
    "# Get segments now\n",
    "segs = scope.get_last_trace_segmented()\n",
    "\n",
    "end = time.time() #perf reasons only\n",
    "\n",
    "print(\"Captured %d segments of %d points each: \"%(len(segs), len(segs[0])))\n",
    "print(\"  %fs capture time\"%(capdone-start))\n",
    "print(\"  %fs dl/proc time\"%(end-capdone))\n",
    "print(\"  %d traces/second\"%(len(segs) / (end-start)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib notebook\n",
    "import matplotlib.pylab as plt\n",
    "\n",
    "#For reference - you can see all the traces here as one (how it's read from teh buffer)\n",
    "\n",
    "wave = scope.get_last_trace()\n",
    "plt.figure()\n",
    "plt.plot(wave)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "seg_len = scope.adc.samples\n",
    "num_seg = int(len(wave) / seg_len)\n",
    "segs = np.reshape(wave[:num_seg*seg_len], (num_seg, seg_len))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Example of reading the segments with `get_last_trace_segmented()` that does the work for you.\n",
    "plt.figure()\n",
    "for seg in segs:\n",
    "    plt.plot(seg[200:300])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
